package com.booza.dao;

import java.util.Collection;
import java.util.List;

import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.booza.constants.DatabaseConstants;
import com.booza.constants.FriendsStatus;
import com.booza.model.Friend;

/**
 * A hibernate DAO class which supports simple CRUD operations on the friends table. 
 * No business logic here. Just the interaction with database.
 * 
 * @see FriendDAO
 */
@Transactional
public class FriendDAOImpl implements FriendDAO
{
	@Autowired
	private SessionFactory sessionFactory;

	public SessionFactory getSessionFactory()
	{
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory)
	{
		this.sessionFactory = sessionFactory;
	}

	/**
	 * Adds friends to the friends table.
	 * 
	 * @param friends
	 *          - list of rows to be inserted
	 */
	public void createFriendsForAccount(List<Friend> friends)
	{
		for (Friend f : friends)
			sessionFactory.getCurrentSession().saveOrUpdate(f);
	}

	public void updateFriendsForAccount(Friend friends)
	{
		sessionFactory.getCurrentSession().update(friends);
	}

	/**
	 * Fetches all friends entry in table.
	 * 
	 * @return Returns all friends.
	 */
	@SuppressWarnings("unchecked")
	public List<Friend> fetchAll()
	{
		return (List<Friend>) sessionFactory.getCurrentSession().createCriteria(Friend.class).list();
	}

	/**
	 * Fetches friends for the given user represented by the <code>accountId</code> parameter.
	 * 
	 * @param accountId
	 *          - id representing the user
	 * @return Return matching friend details.
	 */
	@SuppressWarnings("unchecked")
	public List<Friend> findFriendsForAccount(Integer accountId)
	{
		return (List<Friend>) sessionFactory.getCurrentSession().createCriteria(Friend.class).add(
				Restrictions.eq(DatabaseConstants.ACCOUNT_ID_COLUMN, accountId)).add(
				Restrictions.eq("status", FriendsStatus.ACCEPTED)).list();
	}

	/**
	 * Finds the total number of friends for the user.
	 * 
	 * @param accountId
	 *          - id representing the user
	 * @return Returns a count of friends.
	 */
	public long findFriendsCount(Integer accountId)
	{
		return (Long) sessionFactory.getCurrentSession().createQuery(
				"SELECT COUNT (*) FROM Friend c WHERE c.accountid = :id AND status = 1").setParameter("id", accountId)
				.uniqueResult();
	}

	/**
	 * Finds the friends that the user has.
	 * 
	 * @param accountId
	 *          - id of the user to search for
	 * @return Return a list of all friends for the user.
	 */
	@SuppressWarnings("unchecked")
	public Collection<Friend> searchFriends(Integer accountId)
	{
		return (Collection<Friend>) sessionFactory.getCurrentSession().createCriteria(Friend.class).add(
				Restrictions.or(Restrictions.eq("accountid", accountId), Restrictions.eq("friendid", accountId))).add(
				Restrictions.eq("status", FriendsStatus.ACCEPTED)).list();
	}

	/**
	 * Checks if the user represented by account ID and the user represented by friend ID are already friends.
	 * 
	 * @param accountId
	 *          -id of the user1
	 * @param friendId
	 *          - id other the user2
	 * @return Return true if they are friends, false if they are not.
	 */
	@SuppressWarnings("unchecked")
	public boolean areFriends(Integer accountId, Integer friendId)
	{
		List<Friend> result = sessionFactory.getCurrentSession().createCriteria(Friend.class).add(
				Restrictions.or(Restrictions.eq("accountid", accountId), Restrictions.eq("friendid", accountId))).add(
				Restrictions.or(Restrictions.eq("friendid", friendId), Restrictions.eq("accountid", friendId))).add(
				Restrictions.eq("status", FriendsStatus.ACCEPTED)).list();
		if (result.isEmpty())
			return false;
		else return true;
	}

	/**
	 * Finds list of pending friend requests.
	 * 
	 * @param accountId
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Collection<Friend> findFriendRequests(Integer accountId)
	{
		return (Collection<Friend>) sessionFactory.getCurrentSession().createCriteria(Friend.class).add(
				Restrictions.eq("friendid", accountId)).add(Restrictions.eq("status", FriendsStatus.PENDING)).list();
	}

	/**
	 * Find the row from friends table for the given <code>accountId</code> and <code>friendId</code>.
	 */
	public Friend findFriend(Integer accountId, Integer friendId)
	{
		return (Friend) sessionFactory.getCurrentSession().createCriteria(Friend.class).add(
				Restrictions.eq("friendid", friendId)).add(Restrictions.eq("accountid", accountId)).uniqueResult();
	}

	/**
	 * Find rejected requests by this user.
	 */
	public Friend findRejectRequest(Integer accountId, Integer friendId)
	{
		return (Friend) sessionFactory.getCurrentSession().createCriteria(Friend.class).add(
				Restrictions.eq("accountid", accountId)).add(Restrictions.eq("friendid", friendId)).uniqueResult();
	}
	
	/**
	 * Remove the friend.
	 */
	public void removeFriend(Integer friendId)
	{		
		Friend f = (Friend) sessionFactory.getCurrentSession().load(Friend.class, friendId);
		sessionFactory.getCurrentSession().delete(f);
	}
}
